package discovery

import (
	"context"
	"fmt"
	"log"
	"time"

	clientv3 "go.etcd.io/etcd/client/v3"
)

type ServiceRegistry struct {
	client    *clientv3.Client
	lease     clientv3.Lease
	leaseID   clientv3.LeaseID
	keepAlive <-chan *clientv3.LeaseKeepAliveResponse
}

func NewServiceRegistry() *ServiceRegistry {
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{"localhost:2379"},
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		log.Fatal(err)
	}

	return &ServiceRegistry{
		client: client,
		lease:  clientv3.NewLease(client),
	}
}

func (r *ServiceRegistry) Register(serviceName, addr string, ttl int64) error {
	resp, err := r.lease.Grant(context.Background(), ttl)
	if err != nil {
		return err
	}

	r.leaseID = resp.ID
	key := fmt.Sprintf("/services/%s/%s", serviceName, addr)

	_, err = r.client.Put(context.Background(), key, addr, clientv3.WithLease(r.leaseID))
	if err != nil {
		return err
	}

	r.keepAlive, err = r.lease.KeepAlive(context.Background(), r.leaseID)
	if err != nil {
		return err
	}

	go r.listenKeepAlive()
	return nil
}

func (r *ServiceRegistry) listenKeepAlive() {
	for ka := range r.keepAlive {
		log.Printf("Keep alive response: %v", ka)
	}
}

func (r *ServiceRegistry) Discover(serviceName string) ([]string, error) {
	resp, err := r.client.Get(context.Background(), fmt.Sprintf("/services/%s/", serviceName), clientv3.WithPrefix())
	if err != nil {
		return nil, err
	}

	var addrs []string
	for _, kv := range resp.Kvs {
		addrs = append(addrs, string(kv.Value))
	}
	return addrs, nil
}

func (r *ServiceRegistry) Close() {
	r.client.Close()
}
